package httpd

import (
	"net"
	"errors"
	"strings"
	"fmt"
	"time"
)

const SPLITE = "\r\n"

type HTTPServer struct {
	Server
	Routers map[string](func(*HTTPRequest,*HTTPResponse))
}

type HTTPRequest struct{
	Method string
	Path string
	Protocol string
	Headers map[string]string
	Cookies map[string]string
}

type Cookie struct{
	Value string
	Expires uint64
	MaxAge uint64
	Domain string
	Path string
	Secure bool
	HttpOnly bool
}

type HTTPResponse struct{
	Status int32
	Protocol string
	Version string
	MimeType string
	Charset string
	Headers map[string]string
	Cookies map[string]*Cookie
	Output []byte
}


func NewHTTPResponse() *HTTPResponse {
	return &HTTPResponse{
		Status:200,
		Protocol:"HTTP",
		Version :"1.1",
		MimeType:"text/html",
		Charset:"utf-8",
		Headers:map[string]string{},
		Cookies:map[string]*Cookie{},
	}
}

func NewHTTPServer(host string, port int, num int32) (*HTTPServer, error) {
	ret:= &HTTPServer{
		Server{
			Host:    host,
			Port:    port,
			Num:     num,
		},
		map[string](func(*HTTPRequest,*HTTPResponse)){},
	}
	ret.Handler= HttpHandler(ret)
	return ret,nil
}

func (value *Cookie)ToString(key string)string{
	ret := fmt.Sprintf("Set-Cookie: %s=%s;", key, value.Value)

	if value.Expires!=0{
		ret += fmt.Sprintf(" Expires: %d;", value.Expires)
	}
	if value.MaxAge!=0{
		ret += fmt.Sprintf(" Max-Age: %d;", value.MaxAge)
	}
	if value.Domain!=""{
		ret += fmt.Sprintf(" Domain: %s;", value.Domain)
	}
	if value.Path!=""{
		ret += fmt.Sprintf(" Path: %s;", value.Path)
	}
	if value.Secure{
		ret += " Secure;"
	}
	if value.HttpOnly{
		ret +=  " HttpOnly;"
	}
	ret +=  "\r\n"
	return ret
}

func (hs *HTTPServer)AddRouter(path string, callback func(*HTTPRequest,*HTTPResponse))error{
	hs.Routers[path] = callback
	return nil
}

func ParseProtocol(header string) (string, string, string , int ,error){
	l := len(header)
	last := 0
	for idx := range header{
		if header[idx] == '\r'{
			if idx >= l-1 {
				return "","","",0,errors.New("")
			}
			if header[idx+1] == '\n' {
				line := header[last:idx]
				s:=strings.Split(line," ")
				return s[0],s[1],s[2],idx+2,nil
			}
		}
	}
	return "","","",0,errors.New("")
}

func ParseHeader(header string) (map[string]string,error){
	ret := map[string]string{}
	l := len(header)
	last := 0
	for idx := range header{
		if header[idx] == '\r'{
			if idx >= l-1 {
				return nil,errors.New("")
			}
			if header[idx+1] == '\n' {
				line := header[last:idx]
				if line == "" {
					last = idx+2
					continue
				}
				//fmt.Printf("===>%+v\n", line)
				s:=strings.Split(line,":")
				ret[s[0]]=s[1]
				last = idx+2
			}
		}
	}
	return ret,nil
}


func ParseRequest(Client *net.Conn) (*HTTPRequest,error){
	ret := HTTPRequest{
		Cookies: map[string]string{},
	}
	buffer := make([]byte, 8192)
	var idx int
	_ ,err := (*Client).Read(buffer)
	if err != nil {
		return nil,err
	}
	ret.Method,ret.Path,ret.Protocol,idx,err = ParseProtocol(string(buffer))
		if err != nil {
			return nil,err
		}
		ret.Headers ,err= ParseHeader(string(buffer[idx:]))
		if err != nil {
			return nil,err
		}
		if cookies,ok := ret.Headers["Cookie"];ok{
			cookiesSplit := strings.Split(cookies, ";")
			for idx := range cookiesSplit{
				pair:=strings.Split(cookiesSplit[idx], "=")
				ret.Cookies[pair[0]] = pair[1]
			}
		}
		return &ret,nil
}

func (resp *HTTPResponse)SetStatusCode(code int32 ){
	resp.Status = code
}

func (resp *HTTPResponse)SetCookie(key string ,value *Cookie){
	resp.Cookies[key] = value
}

func (resp *HTTPResponse)SetHeader(key string ,value string){
	resp.Headers[key] = value
}

func (resp *HTTPResponse)Write(content []byte){
	resp.Output = append(resp.Output, content...)
}

func Response(Client *net.Conn, resp *HTTPResponse) error {
	var name string= ""
	if v,ok := HTTP_STATUS[resp.Status];ok {
		name = v
	}
	respText := fmt.Sprintf("%s/%s %d %s\r\n", resp.Protocol, resp.Version, resp.Status, name)
	respText += "Server: tiny\r\n"
	respText += fmt.Sprintf("Date: %s GMT\r\n", time.Now().Format("Mon, 01 Jan 2006 15:04:05"))
	respText += fmt.Sprintf("Content-Type: %s; charset=%s\r\n", resp.MimeType, resp.Charset)
	for k,v := range resp.Headers{
		respText += fmt.Sprintf("%s: %s\r\n",k, v)
	}
	for k,v := range resp.Cookies{
		respText += v.ToString(k)
	}

	respText += "\r\n"
	respText += string(resp.Output)
	_,err:= (*Client).Write([]byte(respText))
	return err
}

func HTTPProtocol(hs *HTTPServer, Client*net.Conn) {
		req,err := ParseRequest(Client)
		if err != nil {
			return 
		}
		fmt.Printf("%+v\n", req)
		resp:= NewHTTPResponse()
		var call (func(*HTTPRequest,*HTTPResponse))
		if v,ok := hs.Routers[req.Path];ok{
			call = v
		}else{
			call = func (req *HTTPRequest, resp *HTTPResponse){
				resp.SetStatusCode(404)
				resp.Write([]byte("file not found"))
			}
		}
		call(req,resp)
		Response(Client, resp)
		(*Client).Close()
}

func HttpHandler(hs *HTTPServer) (func ( *Task)) {
	return func (t *Task){
		HTTPProtocol(hs, t.Client)
	}
}
